package Serveur;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.Iterator;

import Carte.Trajet;
import Carte.Ville;
import Client.Reservation;
import Passager.Passager;
import Serialization.*;
import Serialization.TypeSerialisation;
import Transports.*;

/**
 * Classe contenant toutes les données.
 * 
 * @author belli
 * 
 */
public class Serveur {

	/**
	 * Créer une variable global permettant de savoir
	 * si un serveur  déjà été instancié
	 */
	private static boolean singleton = false;

	/**
	 * Toutes les listes contenant les informations necessaires au fonctionnement
	 * du serveur
	 */
	private ArrayList<Transports> ListeTransport;
	private ArrayList<Trajet> ListeTrajet;
	private ArrayList<Reservation> ListeReservations;
	private ArrayList<Passager> ListePassager;

	private Serialisation Files;

	/**
	 * Construteur normal
	 */
	public Serveur() {
		if (!singleton) {
			singleton = true;
			//Files = new Serialization2();
			
			/*
			 * Remplir les listes avec les données des fichiers
			 */
			//ListeTransport = Files.fillOutListeTransport();
			//ListeTrajet = Files.fillOutListeTrajet();
			//ListeReservations = Files.fillOutListeReservation();
			//ListePassager = Files.fillOutListePassager();
			
			ListePassager = new ArrayList<Passager>();
			ListeReservations = new ArrayList<Reservation>();
			ListeTrajet = new ArrayList<Trajet>();
			ListeTransport = new ArrayList<Transports>();
			
			/*
			 * Supprimer les anciennent reservation
			 */
			ListeReservations = supprimerAnciennesReservations(ListeReservations);
		} else {
			System.out.println("Un serveur existe deja");
		}
		
		System.out.println("Serveur en marche");
	}
	
	/**
	 * Constructeur normal dans lequen peut preciser le 
	 * type de serialisation choisi
	 * @param type de serialisation
	 */
	public Serveur(TypeSerialisation type){
		if (!singleton) {
			singleton = true;
			
			/**
			 * Selon le type de serialisation
			 */
			switch(type){
			case SERIALISATION_OBJECT:
				Files = new SerialisationObject(this);
				break;
			case SERIALISATION_TEXTE:
				Files = new SerialisationText(this);
				break;
			case SERIALISATION_XML:
				Files = new SerialisationXML(this);
				break;
			}
		
			
			/*
			 * Remplir les listes avec les données des fichiers
			 */
			ListeTransport = Files.fillOutListeTransport();
			ListeTrajet = Files.fillOutListeTrajet();
			ListeReservations = Files.fillOutListeReservation();
			//ListePassager = Files.fillOutListePassager();
			
			/*
			 * Supprimer les anciennent reservation
			 */
			//ListeReservations = supprimerAnciennesReservations(ListeReservations);
		} else {
			System.out.println("Un serveur existe deja");
		}
		
		System.out.println("Serveur lancé");
	}

	/**
	 * Accesseur de consultation de la liste des transports
	 * 
	 * @return
	 */
	public ArrayList<Transports> getListeTransport() {
		return ListeTransport;
	}

	/**
	 * Accesseur de consultation de la liste des Reservations
	 * 
	 * @return
	 */
	public ArrayList<Reservation> getListeReservations() {
		return ListeReservations;
	}

	/**
	 * Accesseur de consultation de la liste des Trajet
	 * 
	 * @return
	 */
	public ArrayList<Trajet> getListeTrajet() {
		return ListeTrajet;
	}

	/**
	 * Afficher tous les transport
	 */
	public void displayAllTransport() {
		Iterator<Transports> it = ListeTransport.iterator();
		
		int entierafficher = 0;
		while (it.hasNext()) {
			Transports t = (Transports) it.next();
			System.out.println(entierafficher + " : " + t.toString());
			entierafficher++;
		}
	}

	/**
	 * Afficher la liste des trajets
	 */
	public void displayAllTrajet() {
		Iterator<Trajet> it = ListeTrajet.iterator();

		while (it.hasNext()) {
			Trajet t = (Trajet) it.next();
			t.print();
		}
	}

	/**
	 * Afficher toutes les reservations
	 */
	public void displayAllReservations() {
		Iterator<Reservation> it = ListeReservations.iterator();

		while (it.hasNext()) {
			Reservation res = (Reservation) it.next();
			res.print();
		}
	}

	/**
	 * Rechercher un type de transport dans la liste
	 * @return
	 */
	public ArrayList<Transports> searchTransport(TypeTransport type ) {
		return Transports.searchTransport(ListeTransport, type);
	}

	/**
	 * Ajouter une reservation dans la liste
	 * @param R
	 */
	public void addReservation(Reservation R) {
		ListeReservations.add(R);
	}

	/**
	 * Methode permettant de tester la methode
	 */
	public void viderListes() {
		ListeReservations.clear();
		ListeTrajet.clear();
		ListeTransport.clear();
		ListePassager.clear();
	}

	/**
	 * Modifier un transport
	 * 
	 * @param t
	 * @param i
	 */
	public boolean setTransport(Transports t, int capacite, double prixBillet, TypeTransport type) {
		ArrayList<Transports> ListeTransport = getListeTransport();
		int indexMod = ListeTransport.indexOf(t);
		int idTranport = t.getIdTransport();

		// Verifier que le transport existe dans le serveur
		if (indexMod == -1) {
			return false;
		}
		
		/*
		 * Verifier que les reservations n'ont pas plus de place
		 */
		int trajNbPlace=0;
		for (Trajet traj : ListeTrajet) {
			if(traj.getIdTransport() == t.getIdTransport()){
				int nbplacedispo = traj.getNbPlaceDispo();
				int nbpaceoccupe = this.getTransportListe(traj.getIdTransport()).getCapacite() - nbplacedispo;
				trajNbPlace += nbpaceoccupe;
			}
		}
		
		
		if(capacite < trajNbPlace){
			return false;
		}

		if(capacite == 0){
			capacite = t.getCapacite();
		}
		
		if(prixBillet == 0.0){
			prixBillet = t.getPrixBillet();
		}
		if(type == null){
			type = t.getTypeDuTransport();
		}
		
		// Creer le nouvelle objet transport
		Transports tNew = null;

		//Celon le type de transport
		switch(type){
		case AVION:
			tNew = new Avion(this, idTranport, prixBillet, capacite);
			break;
		case BATEAU:
			tNew = new Bateau(this, idTranport, prixBillet, capacite);
			break;
		case BUS:
			tNew = new Bus(this, idTranport, prixBillet, capacite);
			break;
		case TRAIN:
			tNew = new Train(this, idTranport, prixBillet, capacite);
			break;
		}

		// Modifier l'objet dans la liste
		ListeTransport.set(indexMod, tNew);

		return true;
	}

	/**
	 * Recuperer l'index d'un transport
	 */
	public int getIndexOfTransport(Transports t) {
		return getListeTransport().indexOf(t);
	}

	/**
	 * Recuperer l'index d'un trajet
	 */
	public int getIndexOfTrajet(Trajet t) {
		return getListeTrajet().indexOf(t);
	}

	/**
	 * Recuperer l'index d'un passager
	 */
	public int getIndexOfPassager(Passager t) {
		return getListePassager().indexOf(t);
	}

	/**
	 * Recuperer l'index d'une reservation
	 */
	public int getIndexOfReservation(Reservation t) {
		return getListeReservations().indexOf(t);
	}
	

	/*
	 * =============================
	 * Gerer la liste des transports
	 * =============================
	 */

	/**
	 * Modifier un tranport avec les parametres de modifications
	 */
	public void modifierTransport(Transports old, int newCapacity, double prixBillet, TypeTransport type){
		//recuperer l'amplacement de l'ancien transport
		int idexTrans = this.getIndexOfTransport(old);
		int idTrans = old.getIdTransport();

		Transports newTransports = null;
		switch(type){
		case AVION:
			newTransports = new Avion(this, idTrans, prixBillet, newCapacity);
			break;
		case BATEAU:
			newTransports = new Bateau(this, idTrans, prixBillet, newCapacity);
			break;
		case BUS:
			newTransports = new Bus(this, idTrans, prixBillet, newCapacity);
			break;
		case TRAIN:
			newTransports = new Train(this, idTrans, prixBillet, newCapacity);
			break;
		}
		
		getListeTransport().set(idexTrans, newTransports);
	}

	/**
	 * Modifier un tranport en definisant un nouveau
	 * @param newTransport
	 * @param old
	 */
	public void modifierTransport(Transports newTransport, Transports old){
		//recuperer l'amplacement de l'ancien transport
		int idexTrans = this.getIndexOfTransport(old);

		//Modifier l'identifiant du transport
		newTransport.setIdTransport(old.getIdTransport());
		
		//Modifier le transport dans le serveur
		getListeTransport().set(idexTrans, newTransport);
	}

	/**
	 * Ajouter un transport dans la base de donnée
	 * @param newCapacity
	 * @param prixBillet
	 * @param type
	 */
	public void addTransport(int newCapacity, double prixBillet, TypeTransport type){
		switch(type){
		case AVION:
			new Avion(this, prixBillet, newCapacity);
			break;
		case BATEAU:
			new Bateau(this, prixBillet, newCapacity);
			break;
		case BUS:
			new Bus(this, prixBillet, newCapacity);
			break;
		case TRAIN:
			new Train(this, prixBillet, newCapacity);
			break;
		}
	}

	/**
	 * Ajouter un transport dans la liste
	 * 
	 * @param m
	 */
	public void addTransport(Transports m) {

		// Verifier que le transport n'est pas deja present
		if (getListeTransport().contains(m)) {
			System.out.println("L'objet existe deja dans la liste");
			return;
		}
		
		// ajouter le tansport
		if (!getListeTransport().add(m)) {
			System.out.println("Impossible d'ajouter l'objet");
			return;
		}
	}

	/**
	 * Ajouter un trajet dans la liste
	 * 
	 * @param m
	 */
	public boolean addTrajet(Trajet m) {
		// Verifier si le trajet est present dans la liste
		if (getListeTrajet().contains(m)) {
			System.out.println("L'objet existe d�ja dans la liste");
			return true;
		}
	
		// Ajouter le trajet � la liste
		if (!getListeTrajet().add(m)) {
			System.out.println("Impossible d'ajouter l'objet");
			return true;
		}
	
		return false;
	}

	/**
	 *  Modifier un trajet, les parametres peuvent etre null*
	 * Le trajet est ajouté automatiquement
	 * @param oldtraj
	 * @param villeDepart
	 * @param villeArrive
	 * @param dateDepart
	 * @param dateArrive
	 * @param placeOccuper
	 * @param IDTrans peut etre -1
	 * @param distance
	 */
	public void modifierTrajet(Trajet oldtraj, Ville villeDepart, Ville villeArrive, 
			GregorianCalendar dateDepart, GregorianCalendar dateArrive, int placeOccuper, int IDTrans, int distance){
		ArrayList<Trajet> listeTrajets = getListeTrajet();
		int indexMod = listeTrajets.indexOf(oldtraj);

		// Verifier que le transport existe dans le serveur
		if (indexMod == -1) {
			return;
		}

		// Verifier tous les champs
		GregorianCalendar dateDep2 = dateDepart;
		GregorianCalendar dateArr2 = dateArrive;
		Ville villeDep2 = villeDepart;
		Ville villeArr2 = villeArrive;
		int placeOcc2 = placeOccuper;
		int trans2 = IDTrans;
		int distance2 = distance;

		if (dateDepart == null) {
			dateDep2 = oldtraj.getDatedepart();
		}

		if (dateArrive == null) {
			dateArr2 = oldtraj.getDatearrive();
		}

		if (villeDepart == null) {
			villeDep2 = oldtraj.getVilleDepart();
		}

		if (villeArrive == null) {
			villeArr2 = oldtraj.getVilleArrive();
		}

		if (IDTrans == -1) {
			trans2 = oldtraj.getIdTransport();
		}

		if (placeOccuper == 0) {
			placeOcc2 = oldtraj.getPlaceOcupper();
		}

		if(distance == 0){
			distance2 = oldtraj.getDistance();
		}

		// Creer le nouvelle objet transport
		Trajet newTraj = new Trajet(this, villeDep2, villeArr2, dateDep2, dateArr2,
				trans2, distance2, oldtraj.getIDTrajet());

		//Modifier le numero du trajet
		//
		newTraj.setIDTrajet(oldtraj.getIDTrajet());
		
		//modifier le nombre de place occupée
		//
		newTraj.setPlaceOcupper(placeOcc2);
		
		// Modifier l'objet dans la liste
		listeTrajets.set(indexMod, newTraj);
	}

	/**
	 * Modifier un trajet par un autre
	 * @param newTrajet
	 * @param old
	 */
	public void modifierTrajet(Trajet newTrajet, Trajet old){
		int indexOf = getIndexOfTrajet(old);
		int idTrajet = old.getIDTrajet();
		
		//Modifier l'identifiant du trajet
		newTrajet.setIDTrajet(idTrajet);
		
		//Modifier l'élément dans la liste
		getListeTrajet().set(indexOf, newTrajet);
	}

	/**
	 * Modifier un trajet en connaissant son index
	 * @param newTrajet
	 * @param index
	 */
	public void modifierTrajet(Trajet newTrajet, int index){
		
		//recupere l'identifiant du trajet se trouvant à cette indice
		int IDTrajet = ListeTrajet.get(index).getIDTrajet();
		newTrajet.setIDTrajet(IDTrajet);
		
		//Modifier le trajet dans la liste
		getListeTrajet().set(index, newTrajet);
	}

	/**
	 * Supprimer un transport
	 * 
	 * @param m
	 */
	public void removeTrajet(Trajet t) {
		//rechercher si le trajet est pas present dans un reservation
		for (Reservation res : ListeReservations) {
			if(res.getTrajet().equals(t)){
				System.out.println("Le trajet est present dans un reservation : ne peut etre supprimé");
				return;
			}
		}

		if (!getListeTrajet().remove(t)) {
			System.out.println("Impossible d'enlever l'objet");
		}
	}

	/*
	 * ==================================
	 * Gestion de la liste des passagers
	 * ==================================
	 */

	/**
	 * Obtenir la liste des passagers
	 */
	public ArrayList<Passager> getListePassager() {
		return ListePassager;
	}

	/**
	 * Remplacer la liste des passagers par une autre
	 * @param listePassager
	 */
	public void setListePassager(ArrayList<Passager> listePassager) {
		ListePassager = listePassager;
	}

	/**
	 * Ajouter un passager à la base de donnée
	 * en faisant les verifications qui s'imposent
	 * @param pass
	 */
	public void addPassager(Passager pass){
		//verifier que le passager peut etre ajouté
		if(pass == null){
			System.out.println("Parametre invalide");
			return;
		}

		if(ListePassager.contains(pass)){
			System.out.println("Le passager existe deja");
			return;
		}

		ListePassager.add(pass);
	}
	
	/**
	 * Ajouter un passager à la base de donnée
	 * en faisant les verifications qui s'imposent
	 * @param pass
	 * @return identifiant du passager crée
	 */
	public int addPassager(String nom, String prenom){
		Passager passCourant = new Passager(this, nom, prenom);
		return passCourant.getIdPassager();
	}

	/**
	 * Modifier un passager en lui donnant les inforamtions necessaires
	 * @param pass
	 * @param nom
	 * @param prenom
	 */
	public void setPassager(Passager pass, String nom, String prenom){
		if(nom == null){
			nom = pass.getNom();
		}
		
		if(prenom == null){
			prenom = pass.getPrenom();
		}
		
		pass.setNom(nom);
		pass.setPrenom(prenom);
	}

	/**
	 * Supprimer un passager en verifiant qu'il n'est pas deja present dans
	 * les reseervations.
	 * @param pass
	 */
	public void removePassager(Passager pass){
		//Rechercher si le passager est present dans les reservations
		for (Reservation res : ListeReservations) {
			if(res.getListePassager().contains(pass.getIdPassager())){
				System.out.println("Impossible de supprimer le passager");
				return;
			}
		}

		//Supprimer le passager
		getListePassager().remove(pass);
	}
	
	/**
	 * Supprimer un passager partout!
	 * 
	 * @param pass
	 */
	public void forceRemovePassager(Passager pass){
		//Rechercher si le passager est present dans les reservations
		for (Reservation res : ListeReservations) {
			if(res.getListePassager().contains(pass.getIdPassager())){
				res.supprimerPassager(pass);
			}
		}
		//Supprimer le passager
		getListePassager().remove(pass);
	}

	/**
	 * Supprimer un passager en verifiant qu'il n'est pas déja present dans
	 * les reseervations.
	 * @param pass
	 */
	public void removePassager(int IDPassager){
		Passager pass = getPassagerListe(IDPassager);
		removePassager(pass);
	}


	/**
	 * Supprimer une reservation du serveur
	 * Le trajet retrouve sont nombre de placeOccupe precedent
	 * @param Reservation res
	 */
	public void removeReservation(int IDReservation){
		Reservation res = getReservationListe(IDReservation);
		
		//Recuperer le nombre de place
		//
		int nbPlaceRajoute = res.getListePassager().size();
		int IDTrajet = res.getIdTrajet();
		
		int indexif = getIndexOfReservation(res);
		System.out.println(indexif);
		
		if(indexif != -1){
			getListeReservations().remove(indexif);
			
			//Diminuer le nombre de place dans les trajets serveur
			//
			Trajet mod = getTrajetListe(IDTrajet);
			mod.annulerReservation(nbPlaceRajoute);
		}
}

	/**
	 * Supprimer un transport de la base de donnée
	 * Attention le transport ne doit pas etre present dans les trajet
	 * @param t
	 */
	public boolean removeTransport(Transports t){
		boolean error = false;
	
		//parcourir la liste des trajets
		for (Trajet traj : ListeTrajet) {
			if(traj.getIdTransport() == (t.getIdTransport())){
				error = true;
			}
		}
	
		//Si le trajet vers 
		if(!error){
			getListeTransport().remove(t);
		}
		return error;
	}

	/**
	 * Trouver un passager dans la liste des reservation
	 * @param pass
	 */
	public ArrayList<Reservation> getPassagerReservation(Passager pass){
		ArrayList<Reservation> listenewReservation = new ArrayList<Reservation>();

		//Parcourir la liste des reservation
		for (Reservation res : ListeReservations) {
			if(res.containtPassenger(pass.getIdPassager())){
				listenewReservation.add(res);
			}
		}

		return listenewReservation;
	}

	/**
	 * Obtenir la personne avec un certain identifiant
	 * @param idPersonne
	 * @return
	 */
	public Passager getPassagerListe(int idPersonne){
		//Parcourir la liste des passagers
		for (Passager pass : ListePassager) {
			if(pass.getIdPassager() == idPersonne){
				return pass;
			}
		}		

		return null;
	}

	/**
	 * Recuperer un transport dans la liste en 
	 * fonction de son IDTransport
	 * @param IDTransport
	 * @return
	 */
	public Transports getTransportListe(int IDTransport){
		//Parcourir la liste des transports
		for (Transports tran : ListeTransport) {
			if(tran.getIdTransport() == IDTransport){
				return tran;
			}
		}

		return null;
	}

	/**
	 * Recuperer un trajet dans la liste en 
	 * fonction de son IDTRAJET
	 * @param IDTransport
	 * @return
	 */
	public Trajet getTrajetListe(int IDTrajet){
		//Parcourir la liste des transports
		for (Trajet tran : ListeTrajet) {
			if(tran.getIDTrajet() == IDTrajet){
				return tran;
			}
		}

		return null;
	}

	/**
	 * Recuperer une reservation en fonction de son identifiant
	 * @param IDReservation
	 * @return
	 */
	public Reservation getReservationListe(int IDReservation){
		//Parcourir la liste des reservations
		for (Reservation tran : ListeReservations) {
			if(tran.getIdReservation() == IDReservation){
				return tran;
			}
		}

		return null;
	}

	/**
	 * Savoir si un transport est present dans la liste
	 * @param IDTrajet
	 * @return
	 */
	public boolean containsTransport(int IDTransport){
		if(getTransportListe(IDTransport) == null){
			return false;
		}else{
			return true;
		}
	}

	/**
	 * Savoir si une reservation est present dans la liste
	 * @param IDTrajet
	 * @return
	 */
	public boolean containReservation(int IDReservation){
		if(getReservationListe(IDReservation) == null){
			return false;
		}else{
			return true;
		}
	}

	/**
	 * Savoir si un passager est present dans la liste
	 * @param IDTrajet
	 * @return
	 */
	public boolean containPassager(int IDPassager){
		if(getPassagerListe(IDPassager) == null){
			return false;
		}else{
			return true;
		}
	}

	/**
	 * Savoir si un trajet est present dans la liste
	 * @param IDTrajet
	 * @return
	 */
	public boolean containTrajet(int IDTrajet){
		if(getTrajetListe(IDTrajet) == null){
			return false;
		}else{
			return true;
		}
	}

	/**
	 * Trouver un identifiant libre dans la liste des trajets
	 * @return
	 */
	public int shearchFreeIDTrajet(){
		for(int i=1; i<=ListeTrajet.size(); i++){
			if(!containTrajet(i)){
				return i;
			}
		}
		return 0;
	}

	/**
	 * Obtenir un identifiant valide pour les trajets
	 * @return
	 */
	public int getIdentifiantValideReservation(){
		int taille = ListeReservations.size();
		if(taille == 0){
			return 0;
		}
		
		Reservation traj = ListeReservations.get(taille-1);
		int newId = traj.getIdReservation()+1;

		//Si le numero de trajet existe déjà, il faut en chercher
		//un de libre
		while(containReservation(newId)){
			newId++;
		}

		return newId;
	}

	/**
	 * Trouver un identifiant libre dans la liste des trajets
	 * @return
	 */
	public int shearchFreeIDReservation(){
		for(int i=1; i<=ListeReservations.size(); i++){
			if(!containReservation(i)){
				return i;
			}
		}
		return 0;
	}
	public int getIdentifiantValideTransport(){
		int taille = ListeTransport.size();
		if(taille == 0){
			return 0;
		}
		
		Transports trans = ListeTransport.get(taille-1);
		int newId = trans.getIdTransport()+1;
		
		while(containsTransport(newId)){
			newId++;
		}
		return newId;
	}
	/**
	 * Obtenir un identifiant valide pour les trajets
	 * @return
	 */
	public int getIdentifiantValideTrajets(){
		int taille = ListeTrajet.size();
		if(taille == 0){
			return 0;
		}
		
		Trajet traj = ListeTrajet.get(taille-1);
		int newId = traj.getIDTrajet()+1;
	
		//Si le numero de trajet existe déjà, on incrémente
	
		while(containTrajet(newId)){
			newId++;
		}
	
		return newId;
	}

	public int getIdentifiantValidePassager(){
		int taille = ListePassager.size();
		if(taille == 0){
			return 0;
		}
		
		Passager pass = ListePassager.get(taille-1);
		int newId = pass.getIdPassager()+1;
		
		while(containPassager(newId)){
			newId++;
		}
		
		return newId;
	}

	/**
	 * Méthode permettant de supprimer les anciennes reservations presentent dans le
	 * serveur.
	 * 
	 * @param reservations
	 * @return
	 */
	public static ArrayList<Reservation> supprimerAnciennesReservations(ArrayList<Reservation> reservations) {
		ArrayList<Reservation> nouvellesReservations = new ArrayList<Reservation>();
		GregorianCalendar currentDate = new GregorianCalendar();
		
		if(reservations.size() == 0){
			return nouvellesReservations;
		}
		
		for(int i=0; i<reservations.size(); i++){
			Reservation reservationCourante = reservations.get(i);
			Trajet trajetCourant = reservationCourante.getTrajet();
			GregorianCalendar gc = trajetCourant.getDatearrive();

			if(currentDate.getTimeInMillis()<gc.getTimeInMillis()){
				nouvellesReservations.add(reservationCourante);
			}
		}

		return nouvellesReservations;
	}

	
	/**
	 * Fermer le serveur en serialisant les listes
	 */
	public void closeServeur() {
		singleton = false;
		//Files.serializeServeur(this);
		Files.serializeListe(ListeTransport, ListeTrajet, ListeReservations, ListePassager);
	}

	public void setListeReservations(ArrayList<Reservation> listeReservations) {
		ListeReservations = listeReservations;
	}
}
